{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 常量表达式\n",
    "\n",
    "\n",
    "```{admonition} 需求\n",
    "学习使用 `constexpr` 与 `const`。\n",
    "```\n",
    "\n",
    "```{hint}\n",
    "{guilabel}`视频`\n",
    "\n",
    "- [C++ const 与 constexpr](https://www.bilibili.com/video/BV1Db4y1Y7zF)\n",
    "```\n",
    "\n",
    "**符号常量**\n",
    ":    用于表示那些在初始化后值就不再改变的数值量。\n",
    "\n",
    "    也可以简称为 **常量**。\n",
    "\n",
    "符号常量就是我们认知中的常量。比如圆周率 $\\pi$，在使用过程中，不希望它是可变的，可以使用 **符号常量** 来实现这一目的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "constexpr double pi = 3.1415926; // 定义一个符号常量 pi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[1minput_line_8:2:5: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mcannot assign to variable 'pi' with const-qualified type 'const double'\u001b[0m\n",
      " pi = 7; // 错误，试图改变常量的值\n",
      "\u001b[0;1;32m ~~ ^\n",
      "\u001b[0m\u001b[1minput_line_7:2:19: \u001b[0m\u001b[0;1;30mnote: \u001b[0mvariable 'pi' declared const here\u001b[0m\n",
      " constexpr double pi = 3.1415926; // 定义一个符号常量 pi\n",
      "\u001b[0;1;32m ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~\n",
      "\u001b[0m"
     ]
    },
    {
     "ename": "Interpreter Error",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "Interpreter Error: "
     ]
    }
   ],
   "source": [
    "pi = 7; // 错误，试图改变常量的值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "仅仅是使用常量，而非改变它是可行的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6.2831852"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "double c = 2 * pi;\n",
    "c"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{important}\n",
    "常量对于维护程序的可读性具有重要作用。\n",
    "```\n",
    "\n",
    "```{tip}\n",
    "除了个别情况（例如 0 和 1），程序中应该尽量少用字面常量，而是尽可能使用符号常量。\n",
    "```\n",
    "\n",
    "## 常量表达式\n",
    "\n",
    "**常量表达式**\n",
    ":    仅由常量构成的 **整型值** 表达式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "21"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "constexpr int max = 18; // 字面常量是常量表达式\n",
    "int val = 19;\n",
    "\n",
    "max + 7; // 常量表达式（即常量整数+字面常量）\n",
    "val + 2; // 因为使用了变量，不是常量表达式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一个 `constexpr` 符号常量必须给定一个在编译时已知的值。\n",
    "\n",
    "```c++\n",
    "constexpr int max = 500;\n",
    "\n",
    "void use(int n) {\n",
    "    constexpr int c1 = max + 7; // 正确，c1 是 507\n",
    "    constexpr int c2 = n + 7; // 错误，不知道 c2 的值是多少\n",
    "    // ...\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 只读\n",
    "\n",
    "**只读**\n",
    ":    若某个变量初始化时的值在编译时未知，但初始化后也绝对不改变。\n",
    "\n",
    "C++ 提供了只读变量的表示：`const`。\n",
    "\n",
    "上面的代码可以改写为："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "constexpr int max = 500;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "void use(int n) {\n",
    "    constexpr int c1 = max + 7; // 正确，c1 是 507\n",
    "    const int c2 = n + 7; // 正确，但是 c2 的值可以改变\n",
    "    // ...\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于 `c2` 是一个只读的常量，所以："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[1minput_line_23:5:8: \u001b[0m\u001b[0;1;31merror: \u001b[0m\u001b[1mcannot assign to variable 'c2' with const-qualified type 'const int'\u001b[0m\n",
      "    c2 = 7;\n",
      "\u001b[0;1;32m    ~~ ^\n",
      "\u001b[0m\u001b[1minput_line_23:3:15: \u001b[0m\u001b[0;1;30mnote: \u001b[0mvariable 'c2' declared const here\u001b[0m\n",
      "    const int c2 = n + 7; // 正确，但是 c2 的值可以改变\n",
      "\u001b[0;1;32m    ~~~~~~~~~~^~~~~~~~~~\n",
      "\u001b[0m"
     ]
    },
    {
     "ename": "Interpreter Error",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "Interpreter Error: "
     ]
    }
   ],
   "source": [
    "void use(int n) {\n",
    "    constexpr int c1 = max + 7; // 正确，c1 是 507\n",
    "    const int c2 = n + 7; // 正确，但是 c2 的值可以改变\n",
    "    // ...\n",
    "    c2 = 7; // 错误，c2 是常量\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```{hint}\n",
    "C++98 不支持 `constexpr`，只能使用 `const`。\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++11",
   "language": "C++11",
   "name": "python3100jvsc74a57bd0a2954483fe4136a94bd721da8601251ae4c667fedb6adcfe70ff0183b19bacbc"
  },
  "language_info": {
   "codemirror_mode": "text/x-c++src",
   "file_extension": ".cpp",
   "mimetype": "text/x-c++src",
   "name": "C++11",
   "version": "11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
